/**
 * simple view object
 * created by David hua
 * @2016/7/31
 */
var vo = {};
(function(vo){
	var eventRegExp = new RegExp("[^;./\]{1,}?(?=[:])");
	var plugins = {};
	vo.newObj = function(params){
		if(params.el == null || params.el == '' ){
			throw new Error("the parameter of 'el' is empty");
		}
		var id = params.el;
		if(id.substring(0,1) == '#'){
			this.el = document.getElementById(id.substring(1));
		}else{
			throw new Error('illegal el');
		}
		if(params.parent != null && params.parent != ''
		     && params.parent.substring(0,1) == '#'){
			var parentId = params.parent;
			this.parent = document.getElementById(parentId.substring(1));
			if(this.parent == null){
				throw new Error("couldn't find the parent element whose id '"+id.substring(1)+"'");
			}
		}
		if(this.el == null){
			if(params.html != null && params.html != ''){
				this.el = document.createElement("div");
				this.el.id = id.substring(1);
				this.el.innerHTML = params.html;
			}else{
				throw new Error("id '"+id.substring(1)+"' doesn't have any html content!");
			}
			if(this.parent == null){
				this.parent = document;
			}
			this.parent.appendChild(this.el);
		}
		if(params.datas != null){
			this.setDatas(params.datas);
			this.flush();
		}
		if(params.methods != null&& params.methods instanceof Object){
			this.setMethods(params.methods);
		}
	};
	vo.newObj.prototype = {
		mapper : function(key,value){//map the K/V to view(elements)
			var props = getElementsByAttr_r(this.el,'name',key);
			if(null != props){
				for(var j = 0;j<props.length;j++){
					this.setValue(props[j],value);
				}
			}
		},
		bindMethod : function(){//bind the methods to view(elements)
			//else scan the elements which have attribute named 'vo-m'
			var elements = getElementsByAttr_r(this.el,'vo-m', '[\\w\\W]{1,}');
			for(var i = 0;i<elements.length;i++){
				var str = elements[i].getAttribute('vo-m');//event:method
				var attrs = str.split(";");
				for(var j = 0;j<attrs.length;j++){//type like 'click','blur'....
					var type = eventRegExp.exec(attrs[j]);
					var name = attrs[j].substring(type[0].length+1);
					//if the el is dynamic created,bind the method to its parent elements
					if(typeof(this.methods[name]) != 'undefined'){
						if(this.parent != null){
							vo.events.add(this.el.id,type[0],name,this.methods[name], this.parent);
						}else{//bind the method to the element
							vo.events.add(this.el.id,type,name,this.methods[name],elements[i]);
						}
					}
				}
			}
		},
		setValue : function(element,value){//set value according to tagName,such as 'input','div'..
			if(element.tagName.toLowerCase() == 'input'|| element.tagName.toLowerCase() == 'textarea'){
				var type = null;
				if((type = element.getAttribute("type"))!=null && type == 'checkbox'){
					element.checked = value;
				}else{
					element.value = value;
				}
			}else{
				element.innerHTML = value;
			}
		},
		flush : function(){
			for(var i in this.datas){
				this.mapper(i,this.datas[i]);
			}
		},
		modify : function(key,value){//modify the K/V
			vo.invoke('modify');
			this.datas[key] = value;
			this.mapper(key,value);
		},
		del : function(key){//delete K/V (doesn't affect the HTML code)
			vo.invoke('del');
			delete this.datas[key];
			this.mapper(key,'');
		},
		get : function(key){//get V from K
			vo.invoke('get');
			return this.datas[key];
		},
		add : function(key,value){// add K/V
			vo.invoke('add');
			this.datas[key] = value;
			this.mapper(key,value);
		},
		setDatas : function(datas_new){
			this.datas = datas_new;
		},
		getDatas : function(){
			return this.datas;
		},
		setMethods : function(methods){
			if(typeof(this.methods) == 'undefined'){
				this.methods = {};
			}
			for(var name in  methods){
				this.methods[name] = methods[name].bind(this,this.datas);
			}
			this.bindMethod(this.el);
		},
		trigger:function(name){
			this.methods[name]();
		}
	};
	var events = function(){
		this.typesEVs = {};//三级分类存储events 如:type类型 -> id号 -> name名称 
		this.elementIds = [];
	};
	events.prototype = {
		add:function(id,type,name,ev,element){
			var typeEVs = null;
			if(typeof(typeEVs = this.typesEVs[type])=='undefined'){
				this.typesEVs[type] = {};
				typeEVs = this.typesEVs[type];
			};
			var EVs = null;
			if(typeof(EVs = typeEVs[id]) =='undefined'){
				typeEVs[id] = {};
				EVs = typeEVs[id];
			}
			EVs[name] = ev;
			if(this.elementIds.indexOf(id)<0){
				this.elementIds.push(id);
			}
			if(type == "click"){
				element.onclick = function(){
					if(this.trigger(null,"click")){
						stopBubble();
					}
				}.bind(this);
			}else if(type == "change"){
				element.onchange = function(){
					if(this.trigger(null,"change")){
						stopBubble();
					}
				}.bind(this);
			}else if(type == "focus"){
				element.onfocus = function(){
					if(this.trigger(null,"focus")){
						stopBubble();
					}
				}.bind(this);
			}else if(type == "input"){
				element.oninput = function(){
					if(this.trigger(null,"input")){
						stopBubble();
					}
				}.bind(this);
			}else if(type == "blur"){
				element.onblur = function(){
					if(this.trigger(null,"blur")){
						stopBubble();
					}
				}.bind(this);
			}
		},
		trigger:function(id,type,name,element){
			var source = typeof(element)=='undefined'?target():element;
			if(source != document){
				var attr = source.getAttribute("vo-m");
			}
			var parent = source;
			while((typeof(id) == 'undefined'||id == null)&&parent!=null){//往上查找父节点的id
				id = parent.getAttribute("id");
				parent =  parent.parentNode;
			}
			if(typeof(attr)!='undefined'&&attr!=null){
				var attrs = attr.split(";");
				if(typeof(type)=='undefined'){
					type = eventRegExp.exec(attrs[0])[0];
				}//type like 'click','blur'....
				if(typeof(name) == 'undefined'){
					for(var i = 0;i<attrs.length;i++){
						if(type == eventRegExp.exec(attrs[i])[0]){
							name = attrs[i].substring(type.length+1);
						}
					}
				}
			}
			var typeEVs = this.typesEVs[type];
			var EVs = null;
			console.log('id->'+id+" type->"+type+" name->"+name);
			if(typeof(typeEVs)!='undefined'
					&&typeof(EVs = typeEVs[id])!='undefined'
						&&typeof(EVs[name])!='undefined'){
				vo.invoke(name);
				EVs[name]();
				return true;
			}
			return false;
		},
		remove:function(id,type,name){
			delete this.typesEVs[type][id][name];
		}
	};
	vo.events = new events();
	vo.plugin = function(method,plugin){//add plug-in methods
		plugins[method] = plugin;
	};
	vo.invoke = function(method){//invoke plug-in methods
		var plugin = plugins[method];
		if(typeof(plugin)!='undefined'){
			plugin();
		}
	};
	vo.remove = function(method){//delete plug-in methods
		delete plugins[method];
	};
})(vo);
function getElementsByAttr_r(root,attr,regExp){
	var reg = new RegExp(regExp);
	var elements = root.getElementsByTagName('*');
	var returns = [];
	if(root != document){
		var attribute= root.getAttribute(attr);
		if(attribute!=null&&reg.test(attribute)){
			returns.push(root);
		}
	}
	if(elements != null && elements.length > 0){
		for(var i = 0;i<elements.length;i++){
			var attribute = elements[i].getAttribute(attr);
			if(attribute!=null&&reg.test(attribute)){
				returns.push(elements[i]);
			}
		}
	}
	return returns;
}
function target(){
	var event = window.event||
		arguments.callee.caller.arguments[0];
	var target = event.target||event.srcElement;
	return target;
};
function stopBubble(){
	var event = arguments.callee.caller.arguments[0];
	if(event){
		event.stopPropagation();
	}else if(window.event){
		window.event.cancelBubble = true;
	}
}